import { selectAll, insert, selectByID, selectByIDCommonVoice } from '../dao/voice.js'
import { makeAudio as makeAudioApi } from '../api/tts.js'
import crypto from 'crypto'
import fs from 'fs'
import path from 'path'
import os from 'os'
import { assetPath, ensureConfigInitialized, serviceUrl } from '../config/config.js'
import log from '../logger.js'
import { ipcMain } from 'electron'
import dayjs from 'dayjs'
import { uploadAudio, uploadFile, uploadModelVideo } from '../util/upload'

const MODEL_NAME = 'voice'

export function getAllTimbre() {
  return selectAll()
}

export async function train(path, lang = 'zh', synced = false) {
  // 确保配置初始化完成
  await ensureConfigInitialized();

  const http_url = serviceUrl.baseUrlWithPort
  const tts = serviceUrl.tts
  path = path.replace(/\\/g, '/') // 将路径中的\替换为/
  const param = {
    format: path.split('.').pop(),
    reference_audio: path,
    lang
  }

  try {
    // 第一个 fetch 请求
    const response = await fetch(`${tts}/v1/preprocess_and_tran`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(param)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }

    const res = await response.json();
    log.debug('~ train ~ res:', JSON.stringify(res));

    if (res.code !== 0) {
      return false;
    }

    const { asr_format_audio_url, reference_audio_text } = res;

    if (synced) {
      // 使用 fetch 替代 request.post
      const addResponse = await fetch(`${http_url}/apis/model/api/voice/add`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          origin_audio_path: path,
          lang,
          asr_format_audio_url,
          reference_audio_text
        })
      });

      if (!addResponse.ok) {
        throw new Error(`HTTP error! Status: ${addResponse.status}`);
      }

      const addData = await addResponse.json();
      return addData.data.id;
    } else {
      return insert({ origin_audio_path: path, lang, asr_format_audio_url, reference_audio_text });
    }
  } catch (error) {
    console.error('请求失败:', error);
    throw error; // 重新抛出错误以便调用者可以处理
  }
}

export function makeAudio4Video({voiceId, text, is_common}) {
  return makeAudio({voiceId, text, is_common, targetDir: assetPath.ttsProduct})
}

/**
 * 复制音频文件到目标目录或上传到服务器
 * @param {string} filePath 源文件路径
 * @returns {string} 文件名
 */
export async function copyAudio4Video(filePath) {
  try {
    // 确保配置初始化完成
    await ensureConfigInitialized();

    const http_url = serviceUrl.baseUrlWithPort

    // 检查源文件是否存在
    if (!fs.existsSync(filePath)) {
      throw new Error(`源文件不存在: ${filePath}`);
    }

    // 生成新文件名
    const fileName = dayjs().format('YYYYMMDDHHmmssSSS') + path.extname(filePath);

    // 判断目标路径类型
    if (isHttpUrl(assetPath.ttsProduct)) {
      // 如果是HTTP URL，上传文件到服务器
      log.debug(`准备上传音频文件到服务器: ${filePath}`);

      // 上传文件
      const result = await uploadFile(filePath, `${http_url}/apis/upload/upload/model`, {
        filename: fileName
      });

      log.debug(`音频文件上传成功: ${result.data}`);
      return fileName;
    } else {
      // 如果是本地路径，确保目录存在并复制文件
      const targetDir = assetPath.ttsProduct;

      if (!fs.existsSync(targetDir)) {
        fs.mkdirSync(targetDir, { recursive: true });
      }

      const targetPath = path.join(targetDir, fileName);
      fs.copyFileSync(filePath, targetPath);

      log.debug(`音频文件已复制到: ${targetPath}`);
      return fileName;
    }
  } catch (error) {
    log.error('复制音频文件失败:', error);
    throw error;
  }
}

// 检查路径是否为HTTP URL
function isHttpUrl(path) {
  return path && (path.startsWith('http://') || path.startsWith('https://'));
}

// 创建临时目录
const TEMP_DIR = path.join(os.tmpdir(), 'heygem_audio_temp');
if (!fs.existsSync(TEMP_DIR)) {
  fs.mkdirSync(TEMP_DIR, { recursive: true });
}

export async function makeAudio({voiceId, text, is_common, targetDir}) {  // 确保配置初始化完成
  await ensureConfigInitialized();

  const http_url = serviceUrl.baseUrlWithPort

  const uuid = crypto.randomUUID();
  let voice;
  let reference_audio;
  let reference_text;

  if (is_common === true || is_common === 'true') {
    voice = await selectByIDCommonVoice(voiceId);
    reference_audio = voice.asrFormatAudioUrl;
    reference_text = voice.referenceAudioText;
  } else {
    voice = selectByID(voiceId);
    reference_audio = voice.asr_format_audio_url;
    reference_text = voice.reference_audio_text;
  }

  try {
    // 生成音频
    const audioBuffer = await makeAudioApi({
      speaker: uuid,
      text,
      format: 'wav',
      topP: 0.7,
      max_new_tokens: 1024,
      chunk_length: 100,
      repetition_penalty: 1.2,
      temperature: 0.7,
      need_asr: false,
      streaming: false,
      is_fixed_seed: 0,
      is_norm: 0,
      reference_audio: reference_audio,
      reference_text: reference_text
    });

    // 确定保存位置
    let outputPath;

    if (isHttpUrl(targetDir)) {
      // 如果目标是HTTP URL，保存到临时目录
      if (!fs.existsSync(TEMP_DIR)) {
        fs.mkdirSync(TEMP_DIR, { recursive: true });
      }

      outputPath = path.join(TEMP_DIR, `${uuid}.wav`);
      fs.writeFileSync(outputPath, audioBuffer, 'binary');

      // 上传到服务器 (这里需要实现上传逻辑)
      log.debug('音频已生成到临时目录:', outputPath);

      // 假设这里实现了上传逻辑，上传成功后返回相对路径
      await uploadModelVideo(outputPath, `${uuid}.wav`, `${http_url}`);

      return `${uuid}.wav`;
    } else {
      // 如果目标是本地路径，直接保存
      if (!fs.existsSync(targetDir)) {
        fs.mkdirSync(targetDir, { recursive: true });
      }

      outputPath = path.join(targetDir, `${uuid}.wav`);
      fs.writeFileSync(outputPath, audioBuffer, 'binary');

      log.debug('音频已生成:', outputPath);
      return `${uuid}.wav`;
    }
  } catch (error) {
    log.error('生成音频失败:', error);
    throw error;
  }
}

/**
 * 试听音频
 * @param {string} voiceId
 * @param is_common
 * @param {string} text
 * @returns
 */
export async function audition(voiceId, is_common, text) {
  const tmpDir = require('os').tmpdir()
  console.log("🚀 ~ audition ~ tmpDir:", tmpDir)
  const audioPath = await makeAudio({ voiceId, text, is_common, targetDir: tmpDir })
  return path.join(tmpDir, audioPath)
}

export function init() {
  ipcMain.handle(MODEL_NAME + '/audition', (event, ...args) => {
    return audition(...args)
  })
}
